// @ts-nocheck
/*
 * @Author: zzd993
 * @Date: 2023-02-09 19:25:34
 * @LastEditors: zzd993
 * @LastEditTime: 2023-02-10 16:15:49
 * @FilePath: \手写vue2源码\src\compiler\parse.js
 * Copyright (c) 2023 by ZZD, All Rights Reserved. 
 */

const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*` 
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
const startTagOpen = new RegExp(`^<${qnameCapture}`); // 他配到的组是一个 标签名
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`);
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;
const startTagClose = /^\s*(\/?)>/;
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g

export function parseHTML(html) {

    const ELEMENT_TYPE = 1
    const TEXT_TYPE = 3
    const stack = []  // 用于存放元素
    let currentParent // 指向栈中最后一个元素
    let root

    function createASTElement(tag, attrs) {
        return {
            tag,
            type: ELEMENT_TYPE,
            children: [],
            attrs,
            parent: null
        }
    }

    function start(tag, attrs) {
        let node = createASTElement(tag, attrs)  // 创建一个ast节点
        if(!root) {   // 看一下是否是空树
            root = node  // 如果为空则当前是树的根节点
        }
        if(currentParent) {
            node.parent = currentParent
            currentParent.children.push(node)
        }
        stack.push(node)
        currentParent = node
    }
    function chars(text) { // 文本直接放到当前指向的节点中
        text = text.replace(/\s/g,'')
        text && currentParent.children.push({
            type: TEXT_TYPE,
            text,
            parent: currentParent
        })
    }
    function end() {
        stack.pop()  // 弹出最后一个
        currentParent = stack[stack.length-1]
    }

    function advance(n) {
        html = html.substring(n)
    }
    function parseStartTag() {
        // 比如说是 <div id="app"></div>
        const start = html.match(startTagOpen)
        if(start) {
            const match = {
                tagName: start[1], // 标签名
                attrs: []
            }
            advance(start[0].length)  // 将标签删除
            let attr, end
            // 如果不是开始标签的结束就一直匹配下去,然后将属性干掉
            while(!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
                advance(attr[0].length)
                match.attrs.push({    
                    name: attr[1], 
                    value: attr[3] || attr[4] || attr[5]
                })
            }
            if(end) { // 结束的小尖括号
                advance(end[0].length)
            }
            // console.log(match);
            return match
        }
        
        return false  // 不是开始标签
    }
    // 解析到一个标签删掉一个
    while(html) {  // html最开始肯定是一个<
        // 如果indexOf中的索引是0 说明是一个开始或结束标签
        // >0，说明是文本的结束位置，比如  word<br/>
        let textEnd = html.indexOf('<')
        if(textEnd === 0) {
            const startTagMatch = parseStartTag()  // 开始标签的匹配结果
            if(startTagMatch) {
                start(startTagMatch.tagName, startTagMatch.attrs)
                continue
            }
            let endTagMatch = html.match(endTag)
            if(endTagMatch) {
                advance(endTagMatch[0].length)
                end(endTagMatch[1])
                continue
            }
        }
        // 解析文本
        if(textEnd > 0) {
            let text = html.substring(0, textEnd)  // 文本内容
            if(text) {
                chars(text)
                advance(text.length)  // 解析到的文本  
            }
        }
    }
    console.log(root);
    return root
}

